﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using MPlayer.Common;

namespace FrontDisplay.Music
{
    /// <summary>
    /// Interaction logic for MusicPage.xaml
    /// </summary>
    public partial class MusicPage : UserControl
    {
        private readonly List<Border> _barsL = new List<Border>();
        private readonly List<Border> _barsR = new List<Border>();

        private const int BarCount = 20;
        private const int SpectrumDataSize = 256;
        private readonly DispatcherTimer _specturmTimer;
        private const int SpectrumTimerUpdateInterval = 1000 / 20;
        private const int SpectrumFallStep = 10;

        public MusicPlugin Music { get; private set; }

        public MusicPage(MusicPlugin music)
        {
            Music = music;
            InitializeComponent();
            //TODO: create property to control bottom bar state.
            PositionBar.PositionBarBottom.Visibility = Visibility.Hidden;

            SongChanged(Music.Player.CurrentSong);

            for (int i = 0; i < BarCount; i++)
            {
                _barsL.Add(CreateSpectrumBar(SpectrumL));
                _barsR.Add(CreateSpectrumBar(SpectrumR));
            }

            Music.Player.SongChanged += ((sender, param) => this.InvokedCall(SongChanged, param.Song));
            Music.Player.TimeChanged += ((sender, param) => this.InvokedCall(TimeChanged, param.Time));
            Music.Player.StateChanged += ((sender, param) => this.InvokedCall(StateChanged, param.State));

            Music.Player.CurrentPlayerChanged += ((param) => this.InvokedCall(CurrentPlayerChanged, param));

            CurrentPlayerChanged(Music.Player);

            _specturmTimer = new DispatcherTimer
                {
                    Interval = TimeSpan.FromMilliseconds(SpectrumTimerUpdateInterval)
                };
            _specturmTimer.Tick += SpecturmTimerTick;

            IsVisibleChanged += MusicPageIsVisibleChanged;
        }

        private void SpecturmTimerTick(object sender, EventArgs e)
        {
            SpectrumInfo spectrumInfo = Music.Player.GetSpectrumInfo(SpectrumDataSize);

            if (spectrumInfo != null)
            {
                UpdateSpectrum(spectrumInfo.Left, spectrumInfo.Right);
            }
        }

        private void MusicPageIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue)
            {
                _specturmTimer.Start();
            }
            else
            {
                _specturmTimer.Stop();
            }
        }

        private void CurrentPlayerChanged(IMusicPlayer player)
        {
            PlayerName.Text = player.Name;
            _barsL.ForEach(rectangle => rectangle.Height = 0);
            _barsR.ForEach(rectangle => rectangle.Height = 0);
        }

        /// <summary>
        /// Creates spectrum bar and add it to spectrum panel.
        /// </summary>
        /// <param name="spectrum"></param>
        private Border CreateSpectrumBar(StackPanel spectrum)
        {
            bool isLeft = spectrum == SpectrumL;

            const double eqPanelWidth = 544.0;

            Border bar = new Border
            {
                //Fill = Music.SpectrumBrush,
                Background = Music.SpectrumBrush,
                VerticalAlignment = VerticalAlignment.Bottom, //isLeft ? VerticalAlignment.Bottom : VerticalAlignment.Top,
                Opacity = isLeft ? 0.7 : 0.5,
                //StrokeThickness = 0,
                Width = (eqPanelWidth - BarCount) / BarCount,
                Height = 0,
                Margin = new Thickness(1, 0, 0, 0),
                //Stroke = Brushes.White
            };
            spectrum.Children.Add(bar);

            return bar;
        }

        private void StateChanged(PlayerState state)
        {
            if (state == PlayerState.Playing)
            {
                PlayButton.Template = (ControlTemplate)FindResource("PauseButtonTemplate");
                Music.IdleTimer.Stop();
                //TODO:
                if (Global.MainPanel.Children[0] == InfoPage.Instance)
                {
                    if (((Configuration)Music.Config).IsAutoShowOnPlaying)
                    {
                        Navigation.Navigate(this);
                    }
                }
            }
            else
            {
                if (((Configuration)Music.Config).IsCloseOnIdle)
                {
                    Music.IdleTimer.Start();
                }
                PlayButton.Template = (ControlTemplate)FindResource("PlayButtonTemplate");
            }
        }

        private void TimeChanged(TimeSpan timeSpan)
        {
            if (Music.Player.CurrentSong.Time.TotalSeconds > 0)
            {
                PositionBar.Position = timeSpan.TotalSeconds;
            }
            else
            {
                PositionBar.ClearTime();
            }
        }
        
        private void UpdateSpectrum(float[] dataL, float[] dataR)
        {
            if (dataR.Max() == 0)
            {
                dataR = dataL;
            }
            
            UpdateSpectrumBars(dataL, _barsL);
            UpdateSpectrumBars(dataR, _barsR);
        }

        private static void UpdateSpectrumBars(float[] data, IList<Border> bars)
        {
            const int specHeight = 156;

            int b0 = 0;

            for (int i = 0; i < BarCount; i++) 
		    {
			    double sum = 0.0;

                int b1 = (int)Math.Pow(2, i * 8.0 / (BarCount - 1));

                if (b1 <= b0)
			    {
				    b1 = b0 + 1; // make sure it uses at least 1 FFT bin
			    }

                if (b1 >= SpectrumDataSize)
                {
                    b1 = SpectrumDataSize - 1;
                }
                
                int sc = 10 + b1 - b0;

			    for (;b0<b1;b0++) 
			    {
				    sum += data[b0+1];
			    }

		        int height = (int) ((Math.Sqrt(sum/Math.Log10(sc))*1.7*specHeight)-4.0);
			    height = (height>specHeight ? specHeight : (height<0 ? 0 : height));

                if ((bars[i].Height > height) && (bars[i].Height > SpectrumFallStep))
                {
                    bars[i].Height -= SpectrumFallStep;
                }
                else
                {
                    bars[i].Height = height;
                }
		    }
        }

        private void SongChanged(ISong song)
        {
            string text = song.ToString();
            if (string.IsNullOrEmpty(text))
            {
                SongLabel.Text = null;
                SongLabel2.Text = null;
            }
            else
            {
                SongLabel.Text = song.ToString();

                if (!string.IsNullOrEmpty(song.Artist))
                {
                    //TODO: move 800 into const.
                    if (SongLabel.Size.Width <= 800 - SongLabel.Padding.Left * 2)
                    {
                        SongLabel.Text = song.ToString();
                        SongLabel2.Text = null;
                    }
                    else
                    {
                        SongLabel.Text = song.Artist;
                        SongLabel2.Text = song.Title;

                        //TODO: move 800 into const.
                        if (SongLabel2.Size.Width > 800 - SongLabel2.Padding.Left * 2)
                        {
                            SongLabel2.HorizontalAlignment = HorizontalAlignment.Left;
                        }
                        else
                        {
                            SongLabel2.HorizontalAlignment = HorizontalAlignment.Right;
                        }
                    }
                }
                else
                {
                    SongLabel2.Text = null;
                }

                //Typeface typeface = new Typeface(SongLabel.FontFamily, SongLabel.FontStyle, SongLabel.FontWeight, SongLabel.FontStretch);
                //FormattedText formattedText = new FormattedText(text, System.Globalization.CultureInfo.InvariantCulture,
                //                                                FlowDirection.LeftToRight, typeface, SongLabel.FontSize,
                //                                                SongLabel.Foreground);

                //if (formattedText.Width <= Application.Current.MainWindow.ActualWidth - SongLabel.Padding.Left*2)
                //{
                //    SongLabel.Text = song.ToString();
                //    SongLabel2.Text = null;
                //}
                //else
                //{
                //    SongLabel.Text = song.Artist;
                //    SongLabel2.Text = song.Title;

                //    formattedText = new FormattedText(SongLabel2.Text, System.Globalization.CultureInfo.InvariantCulture,
                //                                                FlowDirection.LeftToRight, typeface, SongLabel2.FontSize,
                //                                                SongLabel2.Foreground);
                //    if (formattedText.Width > Application.Current.MainWindow.ActualWidth - SongLabel2.Padding.Left * 2)
                //    {
                //        SongLabel2.HorizontalAlignment = HorizontalAlignment.Left;
                //    }
                //    else
                //    {
                //        SongLabel2.HorizontalAlignment = HorizontalAlignment.Right;
                //    }
                //}
            }

            PositionBar.Duration = song.Time.TotalSeconds;
        }

        private void PlayButtonClick(object sender, RoutedEventArgs e)
        {
            if (Music.Player.State == PlayerState.Playing)
            {
                Music.Player.Pause();
            }
            else
            {
                Music.Player.Play();
            }
        }

        private void PrevButtonClick(object sender, RoutedEventArgs e)
        {
            Music.Player.Previous();
        }

        private void NextButtonClick(object sender, RoutedEventArgs e)
        {
            Music.Player.Next();
        }

        private void LabelMouseDown(object sender, MouseButtonEventArgs e)
        {
            Navigation.Navigate(Music.PlaylistPage, Animation.MoveDown);
            Music.PlaylistPage.UpdatePlayerInfo();
        }

        private void PositionBarMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point point = e.GetPosition(PositionBar);
            Music.Player.Seek((int)(Music.Player.CurrentSong.Time.TotalSeconds * (point.X / PositionBar.ActualWidth)));
        }

        private void BackButtonClick(object sender, RoutedEventArgs e)
        {
            this.NavigateBack();
        }
    }
}
